#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
离线海图下载诊断工具

这个脚本用于诊断特定区域的瓦片下载问题，测试所有可用瓦片源，
并输出详细的诊断信息帮助排查问题。
"""

import os
import sys
import time
import logging
import json
import argparse
from tqdm import tqdm

# 添加项目根目录到系统路径
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = current_dir
sys.path.append(project_root)

# 导入自定义模块
from backend.downloader import TileDownloader, TILE_SOURCES

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger('DiagnoseTool')

def test_area(min_lat, min_lon, max_lat, max_lon, min_zoom, max_zoom, output_dir="./diagnose_tiles"):
    """测试指定区域的瓦片下载情况"""
    # 确保输出目录存在
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    
    results = {}
    
    # 测试每个瓦片源
    for source_type in TILE_SOURCES.keys():
        source_dir = os.path.join(output_dir, source_type)
        if not os.path.exists(source_dir):
            os.makedirs(source_dir)
        
        logger.info(f"测试瓦片源: {source_type}")
        
        # 创建下载器
        downloader = TileDownloader(
            output_dir=source_dir,
            max_workers=2,
            retries=3,
            timeout=15,
            source_type=source_type
        )
        
        # 仅下载最低缩放级别，做快速测试
        test_zoom = min_zoom
        logger.info(f"下载缩放级别 {test_zoom} 的瓦片进行测试")
        
        # 计算边界瓦片坐标
        min_x, max_y = downloader.deg2num(min_lat, min_lon, test_zoom)
        max_x, min_y = downloader.deg2num(max_lat, max_lon, test_zoom)
        
        # 确保x和y的顺序正确
        min_x, max_x = min(min_x, max_x), max(min_x, max_x)
        min_y, max_y = min(min_y, max_y), max(min_y, max_y)
        
        # 计算测试瓦片数量
        tiles_count = (max_x - min_x + 1) * (max_y - min_y + 1)
        logger.info(f"测试瓦片数量: {tiles_count}")
        
        # 创建下载任务列表
        tasks = []
        for x in range(min_x, max_x + 1):
            for y in range(min_y, max_y + 1):
                tasks.append((test_zoom, x, y))
        
        start_time = time.time()
        downloaded_tiles = 0
        
        # 下载每个瓦片
        for task in tqdm(tasks, desc=f"测试 {source_type} 源", unit="瓦片"):
            tile_path = downloader.download_tile(*task)
            if tile_path is not None:
                downloaded_tiles += 1
        
        elapsed_time = time.time() - start_time
        success_rate = (downloaded_tiles / tiles_count) * 100 if tiles_count > 0 else 0
        
        # 记录结果
        results[source_type] = {
            "tiles_count": tiles_count,
            "downloaded_tiles": downloaded_tiles,
            "success_rate": success_rate,
            "elapsed_time": elapsed_time
        }
        
        logger.info(f"{source_type} 测试完成: {downloaded_tiles}/{tiles_count} 瓦片下载成功 ({success_rate:.2f}%), 耗时: {elapsed_time:.2f}秒")
    
    # 输出结果摘要
    logger.info("\n测试结果摘要:")
    
    # 按成功率排序
    sorted_results = sorted(results.items(), key=lambda x: x[1]["success_rate"], reverse=True)
    
    for source_type, result in sorted_results:
        logger.info(f"{source_type}: {result['success_rate']:.2f}% 成功率, {result['downloaded_tiles']}/{result['tiles_count']} 瓦片, {result['elapsed_time']:.2f}秒")
    
    # 推荐最佳瓦片源
    if sorted_results:
        best_source = sorted_results[0][0]
        best_rate = sorted_results[0][1]["success_rate"]
        
        if best_rate > 0:
            logger.info(f"\n推荐使用 {best_source} 瓦片源，成功率: {best_rate:.2f}%")
            
            # 计算估计的完整下载时间
            total_tiles = 0
            for z in range(min_zoom, max_zoom + 1):
                min_x, max_y = downloader.deg2num(min_lat, min_lon, z)
                max_x, min_y = downloader.deg2num(max_lat, max_lon, z)
                min_x, max_x = min(min_x, max_x), max(min_x, max_x)
                min_y, max_y = min(min_y, max_y), max(min_y, max_y)
                tiles_at_zoom = (max_x - min_x + 1) * (max_y - min_y + 1)
                total_tiles += tiles_at_zoom
            
            # 估计下载时间 (基于测试结果的平均时间)
            avg_tile_time = sorted_results[0][1]["elapsed_time"] / sorted_results[0][1]["tiles_count"] if sorted_results[0][1]["tiles_count"] > 0 else 0
            est_download_time = avg_tile_time * total_tiles / downloader.max_workers  # 考虑并行下载
            
            logger.info(f"全部缩放级别共 {total_tiles} 个瓦片，估计下载时间: {est_download_time/60:.1f}分钟")
        else:
            logger.warning("\n所有瓦片源测试均失败，请检查网络连接或区域设置")
    
    return results

def diagnose_task(task_id):
    """诊断特定任务的下载问题"""
    task_dir_name = f"task_{task_id}"
    task_dir = os.path.join(project_root, "data", "tiles", task_dir_name)
    
    if not os.path.exists(task_dir):
        logger.error(f"任务目录不存在: {task_dir}")
        return
    
    # 检查任务的边界信息
    boundary_file = os.path.join(task_dir, "boundary.json")
    if not os.path.exists(boundary_file):
        logger.error(f"边界信息文件不存在: {boundary_file}")
        return
    
    try:
        with open(boundary_file, 'r') as f:
            boundary = json.load(f)
        
        min_lat = boundary.get('min_lat')
        min_lon = boundary.get('min_lon')
        max_lat = boundary.get('max_lat')
        max_lon = boundary.get('max_lon')
        min_zoom = boundary.get('min_zoom')
        max_zoom = boundary.get('max_zoom')
        
        if None in (min_lat, min_lon, max_lat, max_lon, min_zoom, max_zoom):
            logger.error("边界信息不完整")
            return
        
        logger.info(f"任务 #{task_id} 边界信息: {min_lat},{min_lon} - {max_lat},{max_lon}, 缩放级别: {min_zoom}-{max_zoom}")
        
        # 检查区域是否主要是海域，统一调用TileDownloader
        tmp_downloader = TileDownloader()
        is_ocean = tmp_downloader.is_ocean_area(min_lat, min_lon, max_lat, max_lon)
        if is_ocean:
            logger.info("对于海域地图，推荐使用 esri_ocean 或 google_satellite 瓦片源")
        
        # 测试各瓦片源
        logger.info(f"开始诊断任务 #{task_id}，测试各瓦片源...")
        test_area(min_lat, min_lon, max_lat, max_lon, min_zoom, min(min_zoom+2, max_zoom))
        
    except Exception as e:
        logger.error(f"诊断过程中出错: {str(e)}")
        import traceback
        logger.error(traceback.format_exc())

def main():
    """主函数"""
    parser = argparse.ArgumentParser(description='离线海图下载诊断工具')
    parser.add_argument('--task', type=int, help='要诊断的任务ID')
    parser.add_argument('--area', nargs=4, type=float, metavar=('MIN_LAT', 'MIN_LON', 'MAX_LAT', 'MAX_LON'),
                        help='要测试的区域边界经纬度')
    parser.add_argument('--zoom', nargs=2, type=int, default=[5, 10], metavar=('MIN_ZOOM', 'MAX_ZOOM'),
                        help='缩放级别范围 (默认: 5 10)')
    
    args = parser.parse_args()
    
    if args.task is not None:
        diagnose_task(args.task)
    elif args.area:
        min_lat, min_lon, max_lat, max_lon = args.area
        min_zoom, max_zoom = args.zoom
        
        logger.info(f"测试区域: {min_lat},{min_lon} - {max_lat},{max_lon}, 缩放级别: {min_zoom}-{max_zoom}")
        test_area(min_lat, min_lon, max_lat, max_lon, min_zoom, max_zoom)
    else:
        parser.print_help()

if __name__ == "__main__":
    main() 